package br.unicamp.mc715.transport;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;

/**
 * This class implements a {@link TransportId} for the UDP transport. <p>
 */
public class UDPTransportId implements TransportId {
	private static final long serialVersionUID = -1769789135808428291L;
	
	/** The SocketAddress identified by this {@link TransportId} */
	InetSocketAddress address;
	
	
    /**
     * Creates a new transport id with the provided ip address and port. <p>
     * 
     * @param address the raw Internet protocol address of the transport in
     *                network byte order (highest order byte at index 0).
     * @param port the port the transport listens to.
     */
    public UDPTransportId(byte[] address, int port) {
    	try {
    		this.address = new InetSocketAddress(InetAddress.getByAddress(address), port);
    	} catch (IOException e) {
    		throw new RuntimeException(e);
    	}
    }
    
    /**
     * Creates a new transport id with the provided Socket Address (address and port). <p>
     * 
     * @param address the socket address of the transport
     */
    public UDPTransportId(InetSocketAddress address) {
    	this.address = address;
    }
    
    /**
     * Creates a new transport id, represented by the string in "host:port" format.
     * 
     * @param address the socket address of the transport, formated as "host:port"
     * @throws IllegalArgumentException If the transportId is invalid
     */
    public UDPTransportId(String address) throws IllegalArgumentException{
    	try {
	    	String[] split = address.split(":");
	    	if (split.length != 2)
	    		throw new Exception("Wrong number of parts: " + split.length + ". 2 expected");
	    	this.address = new InetSocketAddress(split[0], Integer.parseInt(split[1]));
    	} catch (Throwable t) {
    		throw new IllegalArgumentException("Malformed UDPTransportID: " + address);
    	}
    }
    
    /**
     * Returns the raw Internet protocol address of the transport. <p>
     * 
     * @return the raw Internet protocol address of the transport in
     *         network byte order (highest order byte at index 0).
     */
    public byte[] getAddress() {
    	return address.getAddress().getAddress();
    }
    
    /**
     * Returns the port the transport listens to. <p>
     * 
     * @return the port the transport listens to.
     */
    public int getPort() {
    	return address.getPort();
    }

    /**
     * Returns the socket address the transport listens to. <p>
     * 
     * @return the host:port the transport listens to.
     */
    public InetSocketAddress getSocketAddress() {
    	return address;
    }

    public boolean equals(Object o) {
    	if (o instanceof UDPTransportId) {
    		return compareTo((TransportId) o) == 0;
    	} else {
    		return false;
    	}
    }

    public int hashCode() {
    	return Arrays.hashCode(getAddress()) ^ getPort();
    }

    public int compareTo(TransportId tId) {
    	UDPTransportId udp = (UDPTransportId) tId; // throws ClassCastException if needed.
    	
    	byte[] ipA = this.getAddress();
    	byte[] ipB = udp.getAddress();
    	
    	// Compare os IPs, byte-a-byte.
    	for (int i=0; i < 4; i++) {
    		if (ipA[i] < ipB[i])
    			return -1;
    		else if (ipA[i] > ipB[i])
    			return 1;
    	}
    	
    	//Em caso de empate de IP, compara as portas
    	return this.getPort() - udp.getPort();
    }

    public String toString() { 
    	String[] res = address.toString().split("/");
    	return res[1];
    }
}
